﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;

// TODO: ljm >>> 需要梳理代码和继承逻辑
public class ManagerLogic
{
    private const float SLOW_DELTA_TIME = 1f;
    
    private List<IManager> ManagerList = new List<IManager>();
    private List<IManager> ManagerInitedList = new List<IManager>();
    private List<IManager> ManagerEnterSceneList = new List<IManager>();
    private HashSet<IUpdate> updateLoged = new HashSet<IUpdate>();
    private HashSet<ILateUpdate> lateUpdateLoged = new HashSet<ILateUpdate>();
    private HashSet<ISlowUpdate> slowUpdateLoged = new HashSet<ISlowUpdate>();
    private List<IUpdate> UpdateList = new List<IUpdate>();
    private List<ILateUpdate> LatUpdateList = new List<ILateUpdate>();
    private List<ISlowUpdate> SlowUpdateList = new List<ISlowUpdate>();
    private float slowUpdateTimeStamp = 0;

    private static ManagerLogic instance;
    public static ManagerLogic Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new ManagerLogic();
            }

            return instance;
        }
    }
    
    
    public void OnAwake()
    {
        RegisterManager(BattleObjectManager.Instance);
        RegisterManager(UIPageManager.Instance);
    }
    
    private void RegisterManager<T>(Manager<T> manager) where T : class, new()
    {
        if (manager != null)
        {
#if UNITY_EDITOR
            for (int i = 0; i < ManagerList.Count; i++)
            {
                if (ManagerList[i].GetType() == manager.GetType())
                {
                    GLog.Error("[ManagerLogic] 重复注册的单例类！" + manager.GetType());
                    return;
                }
            }
#endif
            manager.OnAwake();
            ManagerList.Add(manager);
            
            if (manager is IUpdate update)
                UpdateList.Add(update);
            if (manager is ILateUpdate lateUpdate)
                LatUpdateList.Add(lateUpdate);
            if (manager is ISlowUpdate slowUpdate)
                SlowUpdateList.Add(slowUpdate);
        }
    }
    
    public void OnInit()
    {
        for (int i = 0, len = ManagerList.Count; i < len; i++)
        {
            try
            {
                ManagerList[i].OnInit();
            }
            catch (Exception e)
            {
                GLog.Error("[ManagerLogic] OnEvent Error！" + e.ToString());
            }
        }
    }

    public void OnReset()
    {
        
    }
    
    public void OnEnterGame(bool bReconnect)
    {
        for (int i = 0, len = ManagerList.Count; i < len; i++)
        {
            var manager = ManagerList[i];
            try
            {
                if (!ManagerInitedList.Contains(manager))
                {
                    manager.OnEnterGame(bReconnect);
                    ManagerInitedList.Add(manager);
                }
                else
                {
                    GLog.Error("[ManagerLogic] OnEnterGame/OnExitGame 不成对！" + manager.GetType());
                }
            }
            catch (Exception e)
            {
                GLog.Error("[ManagerLogic] OnInit Error！" + e.ToString());
            }
        }
    }
    
    public void OnExitGame(bool bReconnect)
    {
        int i = ManagerInitedList.Count;
        while (--i > -1)
        {
            var manager = ManagerInitedList[i];
            try
            {
                if (ManagerEnterSceneList.Contains(manager))
                {
                    ManagerEnterSceneList.Remove(manager);
                    manager.OnExitScene(true);
                }
                
                manager.OnExitGame(bReconnect);
            }
            catch (Exception e)
            {
                GLog.Error("[ManagerLogic] OnReset Error！" + e.ToString());
            }
            ManagerInitedList.RemoveAt(i);
        }
        
        updateLoged.Clear();
        slowUpdateLoged.Clear();
        lateUpdateLoged.Clear();
    }
    

    public void OnPause(bool paused)
    {
        if (ManagerList.Count < 1) return;
        for (int i = 0, len = ManagerList.Count; i < len; i++)
        {
            try
            {
                ManagerList[i].OnPause(paused);
            }
            catch (Exception e)
            {
                GLog.Error("[ManagerLogic] OnPause Error！" + e.ToString());
            }
        }
    }

    public void OnFocus(bool focus)
    {
        if (ManagerInitedList.Count < 1) return;
        for (int i = 0, len = ManagerInitedList.Count; i < len; i++)
        {
            try
            {
                ManagerInitedList[i].OnFocus(focus);
            }
            catch (Exception e)
            {
                GLog.Error("[ManagerLogic] OnFocus Error！" + e.ToString());
            }
        }
    }

    public void OnUpdate()
    {
        var deltaTime = Time.deltaTime;
        
        slowUpdateTimeStamp = slowUpdateTimeStamp + deltaTime;
        if (SlowUpdateList.Count > 0 && slowUpdateTimeStamp >= SLOW_DELTA_TIME)
        {
            slowUpdateTimeStamp -= SLOW_DELTA_TIME;
            for (int i = 0; i < SlowUpdateList.Count; i++)
            {
                try
                {  
                    SlowUpdateList[i].OnSlowUpdate(SLOW_DELTA_TIME);
                }
                catch (Exception e)
                {
                    if (!slowUpdateLoged.Contains(SlowUpdateList[i]))
                    {
                        slowUpdateLoged.Add(SlowUpdateList[i]);
                        GLog.Error("[ManagerLogic] SlowUpdate Error！" + e.ToString());
                    }
                }
                
            }
        }

        if (UpdateList.Count > 0)
        {
            for (int i = 0; i < UpdateList.Count; i++)
            {
                try
                {
                    UpdateList[i].OnUpdate(deltaTime);
                }
                catch (Exception e)
                {
                    if (!updateLoged.Contains(UpdateList[i]))
                    {
                        updateLoged.Add(UpdateList[i]);
                        GLog.Error("[ManagerLogic] Update Error！" + e.ToString());
                    }
                }
            }
        }
    }
    
    public void OnLateUpdate()
    {
        if (LatUpdateList.Count < 1) return;
        for (int i = 0;i<LatUpdateList.Count; i++)
        {
            try
            {
                LatUpdateList[i].OnLateUpdate(Time.deltaTime);
            }
            catch (Exception e)
            {
                if (!lateUpdateLoged.Contains(LatUpdateList[i]))
                {
                    lateUpdateLoged.Add(LatUpdateList[i]);
                    GLog.Error(e.ToString());
                }
            }
        }
    }
    
    public void OnDestroy()
    {
        for (int i =ManagerList.Count-1; i>=0; i--)
        {
            try
            {
                ManagerList[i].OnDestroy();
            }
            catch (Exception e)
            {
                GLog.Error(e.ToString());
            }
        }

        ManagerList.Clear();
        ManagerInitedList.Clear();
        UpdateList.Clear();
     //   FixUpdateList.Clear();
        SlowUpdateList.Clear();
        LatUpdateList.Clear();
        slowUpdateTimeStamp = 0;
    }

    private StackTrace onenterSceneStack;
    
    public void OnExitScene(bool bNewScene)
    {
        onenterSceneStack = null;
        
        if (ManagerInitedList.Count < 1) return;
        for (int i = 0, len = ManagerInitedList.Count; i < len; i++)
        {
            try
            {
                var manager = ManagerInitedList[i];
                if (ManagerEnterSceneList.Contains(manager))
                {
                    ManagerEnterSceneList.Remove(manager);
                    manager.OnExitScene(bNewScene);
                }
            }
            catch (Exception e)
            {
                GLog.Error(e.ToString());
            }
        }
    }

    public void OnEnterScene()
    {
        StackTrace last = onenterSceneStack;
        onenterSceneStack = new StackTrace(2);
        if (last != null)
        {
            GLog.Info("OnEnterScene 不成对\n {0}\n new \n{1}", last.ToString(), onenterSceneStack.ToString());
            last = null;
        }
        
        if (ManagerInitedList.Count < 1) return;
        for (int i = 0, len = ManagerInitedList.Count; i < len; i++)
        {
            try
            {
                var manager = ManagerInitedList[i];
                if (!ManagerEnterSceneList.Contains(manager))
                {
                    ManagerEnterSceneList.Add(manager);
                    manager.OnEnterScene();
                }
            }
            catch (Exception e)
            {
                GLog.Error(e.ToString());
            }
        }
    }
    
    public void OnDisconnect()
    {
        if (ManagerInitedList.Count < 1) return;
        for (int i = 0, len = ManagerInitedList.Count; i < len; i++)
        {
            try
            {
                ManagerInitedList[i].OnDisconnect();
            }
            catch (Exception e)
            {
                GLog.Error(e.ToString());
            }
        }
    }
}